package cn.birdbaby.plugins.selector;

import cn.birdbaby.plugins.selector.clazz.ClassSelectorRegistry;
import cn.birdbaby.plugins.selector.field.FieldSelectorRegistry;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.util.PsiTreeUtil;
import org.apache.commons.lang3.ArrayUtils;

import java.util.*;

/**
 * @author llorando 9/8/24 1:02 pm
 * <p>
 * foreach->annotator.match 适合链路短的场景
 * selector->matcher->annotator 职责更加分离，容易重构拓展
 * todo 流式编程重构，提高效率（处理1G以上的项目时会卡顿）
 */
public abstract class PsiElementSelector {
    public Map<PsiJavaFile, List<PsiElement>> selectPsiElement(PsiElement psiElement) {
        //选中文件操作
        if (psiElement instanceof PsiJavaFile) {
            return matchInFile((PsiJavaFile) psiElement);
        }
        //选中文件夹操作
        else if (psiElement instanceof PsiDirectory) {
            return matchInDir((PsiDirectory) psiElement);
        }
        return defaultReturn();
    }

    protected Map<PsiJavaFile, List<PsiElement>> matchInFile(PsiFile javaFile) {
        if (match(javaFile)) {
            Map map = new HashMap();
            map.put(javaFile, Arrays.asList(javaFile));
            return map;
        } else return defaultReturn();
    }

    protected Map<PsiJavaFile, List<PsiElement>> matchInDir(PsiDirectory psiElement) {
        Map<PsiJavaFile, List<PsiElement>> match = new HashMap<>();
        Collection<PsiDirectory> childrenOfAnyType = PsiTreeUtil.findChildrenOfAnyType(psiElement, PsiDirectory.class);
        PsiFile[] files = psiElement.getFiles();
        if (!ArrayUtils.isEmpty(files)) {
            for (PsiFile psiFile : files) {
                match.putAll(matchInFile(psiFile));
            }
        }
        //递归完成元素的添加
        for (PsiDirectory directory : childrenOfAnyType) {
            match.putAll(matchInDir(directory));
        }

        return match;
    }

    protected Map<PsiJavaFile, List<PsiElement>> defaultReturn() {
        return Collections.emptyMap();
    }

    public abstract boolean match(PsiElement psiElement);

    //todo 不能用class引用 这样要依赖许多第三方包
    public abstract String getName();

    public static PsiElementSelector getSelector(SelectType type,String selectorName){
        switch (type){
            case CLASS :
                return ClassSelectorRegistry.find(selectorName);
            case FIELD :
                return FieldSelectorRegistry.find(selectorName);
            default :
                throw new RuntimeException(type+"类型找不到"+selectorName);
        }
    }
}
